<?php
/*
	数据库备份还原
	备份方法：
		分批导出数据，生成插入sql，将相应代码存入sql
		每个文件保存数据最多10M，超出则导入下一个文件
		逐表获取数据，每次获取500条数据
*/
defined('BY_JYA') or exit('error');
$_RQ['op']=$_RQ['op']?$_RQ['op']:'list';
$back_dir=ROOT_AT.'backup/';
if(!is_dir($back_dir)){
	mkdir($back_dir);
}
if(!function_exists('scandir')){
	exi('请开启scandir函数支持！','error','goback');
}
$tabs=array(
	array('op'=>'list','title'=>'数据库备份列表'),
	array('op'=>'add','title'=>'创建数据库备份'),
	array('op'=>'repair','title'=>'优化数据表'),
	array('op'=>'rundb','title'=>'运行数据库'),
);
set_html('tabs',$tabs);
if($_RQ['op']=='list'){
	$backs=scandir($back_dir);
	$list=array();
	foreach($backs as $key=>$value){
		if($value=='.' || $value=='..'){
			continue;
		}
		$suns=scandir($back_dir.$value);
		if(is_file($back_dir.$value.'/isend.txt')){
			$isend=1;
			$volume=count($suns)-3;
		}else{
			$isend=0;
			$volume=count($suns)-2;
		}
		$list[]=array(
			'time'=>$value,
			'name'=>$value,
			'volume'=>$volume,
			'isend'=>$isend
		);	
	}
	paixu($list,'name','desc');
	$return['list']=$list;
	$return['htmls']=array(
		'type'=>'list',
		'list'=>array('data'=>'list','delall'=>false,'del_one'=>false,'edit'=>false,'key'=>'name','list'=>array(
			array('type'=>'string','title'=>'备份名称','name'=>'name'),
			array('type'=>'time','title'=>'备份时间','name'=>'time'),
			array('type'=>'string','title'=>'分卷数量','name'=>'volume'),
			array('type'=>'radio','title'=>'备份完整？','name'=>'isend','options'=>'isend'),			
		),'action'=>array(
			array('type'=>'post','op'=>'down','title'=>'下载此备份'),
			array('type'=>'post','op'=>'back','show'=>array('isend'=>$isend),'message'=>'确定还原此备份？还原后将覆盖当前所有数据，请谨慎操作！','title'=>'还原此备份'),
			array('type'=>'post','op'=>'del','message'=>'确定删除此备份？删除后将无法找回！','title'=>'删除此备份'),
		)),
	);
	$return['isend']=array('不完整','完整');
	exi($return);
}
if($_RQ['op']=='down'){
	$url=download_url('core/system.db/download/name='.$_RQ['name']);
	exi('','',$url);
}
if($_RQ['op']=='download'){
	//下载备份
	$name=trim($_RQ['name']);
	if(!$name){
		exi('请先选择要下载的备份','error');
	}
	$dir=$back_dir.$name;
	if(!is_dir($dir)){
		exi('指定备份不存在或已被下载！','error');
	}
	do{
		$zip=ROOT_AT.random(10).'.temp';
	}while(is_file($zip));
	cfc('files')->zip($dir,$zip);
	header('content-type: application/zip');
	header('content-disposition: attachment; filename="' . $name . '.zip"');
	readfile($zip);
	@unlink($zip);
	exit();
}
if($_RQ['op']=='back'){
	//恢复备份，逐个获取备份sql执行之
	set_time_limit(0);
	$name=trim($_RQ['name']);
	if(!$name){
		exi('请先选择要恢复的备份','error');
	}
	if(!is_dir($back_dir.$name)){
		exi('指定备份不存在！','error');
	}
	$restore_volume_sizes = max(1, intval($_RQ['volume_sizes']));
	$restore_volume_name = model('core/db_tools')->system_database_backup($name,$restore_volume_sizes);
	if (!$restore_volume_name){
		exi('成功恢复数据备份,可能还需要更新缓存。','','list');
	}
	model('core/db_tools')->system_database_volume_restore($restore_volume_name);
	$volume_sizes=$restore_volume_sizes;
	++$restore_volume_sizes ;
	exi('正在恢复数据备份, 请不要关闭浏览器, 当前第' . $volume_sizes . ' 卷.','', 'core/system.db/back/volume_sizes='.$restore_volume_sizes.'&name='.$name);
}
if($_RQ['op']=='del'){
	$name=trim($_RQ['name']);
	if(!$name){
		exi('请先选择要删除的备份','error');
	}
	if(!is_dir($back_dir.$name)){
		exi('指定备份不存在或已被删除！','error');
	}
	cfc('files')->rmdirs($back_dir.$name);
	exi('删除成功！');
}
if($_RQ['op']=='add'){
	//创建备份
	//$tables=pdo_fetchall("SHOW TABLE STATUS LIKE '".TABLE_PRE."%'");
	$return['list']=$tables;
	$return['total']=count($tables);
	$current = array(
			'last_table' => '',
			'index' =>0,
			'series' =>1,
			'back'=>TIMESTAMP
	);
	$return['current']=$current;
	exi($return);
}
if($_RQ['op']=='add_table'){
	$post=get_postdata();
	$back=trim($post['back']);
	$series = max(1, intval($post['series']));
	if ($series==1) {
		mkdir($back_dir.$back);
		$system=core_setting('system');
		$system['backsqldata']=$post['back'];
		core_setting('system',$system);
	}
	$size = 300;
	$volumn = 1024 * 1024 * 2;
	$dump = '';
	if (empty($post['last_table'])) {
			$last_table = '';
			$catch = true;
	} else {
			$last_table = $post['last_table'];
			$catch = false;
	}
	$tables=pdo_fetchall("SHOW TABLE STATUS LIKE '".TABLE_PRE."%'");
	foreach($tables as $table){
		$tablename=array_shift($table);
		$name=explode('_',$tablename,2);
		if($name=='core_access_log'){
			//访问日志表内容过多，此处不做处理
			continue;
		}
		if (!empty($last_table) && $tablename == $last_table) {
			$catch = true;
		}
		if (!$catch) {
			continue;
		}
		if (!empty($dump)) {
			$dump .= "\n\n";
		}
		if ($tablename != $last_table) {
			$row = model('core/db_tools')->db_table_schemas($tablename);
			$dump .= $row;
		}
		$index = 0;
		if (!empty($post['index'])) {
				$index = intval($post['index']);
				$post['index'] = 0;
		}while (true) {
				$start = $index * $size;
				$result = model('core/db_tools')->db_table_insert_sql($tablename, $start, $size);
				if (!empty($result)) {
					$dump .= $result['data'];
					if (strlen($dump) > $volumn) {
						$bakfile = $back_dir.$back.'/volume-'.$series.'.sql';
						$dump .= "\n\n";
						file_put_contents($bakfile, $dump);
						++$series;
						++$index;
						$current = array(
							'last_table' => $tablename,
							'index' => $index,
							'series' => $series,
							'back'=>$back,
						);
						$return['current']=$current;
						exi($return);
					}
				}
				if (empty($result) || count($result['result']) < $size) {
					break;
				}
				++$index;
			}
	}
	$bakfile = $back_dir.$back.'/volume-'.$series.'.sql';
	$dump .= "\n\n----JinYun MySQL Dump End";
	file_put_contents($bakfile, $dump);
	$bakfile = $back_dir.$back.'/isend.txt';
	file_put_contents($bakfile, 'JinYun MySQL Dump End');
	$return['current']=array('isend'=>1);
	exi($return);
}
if($_RQ['op']=='repair'){
	$repair_table = array();
	$sql = "SHOW TABLE STATUS LIKE '".TABLE_PRE."%'";
	$tablelist = pdo_fetchall($sql);
	if($tablelist){
			foreach ($tablelist as $tableinfo) {
				if ($tableinfo['Engine'] == 'InnoDB') {
					continue;
				}
				if($tableinfo['Name']=='ims_core_session' && $tableinfo['Engine'] != 'InnoDB'){
					$updb = "ALTER TABLE ims_core_session ENGINE=InnoDB";
					pdo_fetch($updb);
				}
				if (!empty($tableinfo) && !empty($tableinfo['Data_free'])) {
					$row = array(
						'id' => $tableinfo['Name'],
						'type' => $tableinfo['Engine'],
						'rows' => $tableinfo['Rows'],
						'size' => fun_size($tableinfo['Data_length']),
						'index' => fun_size($tableinfo['Index_length']),
						'free' => fun_size($tableinfo['Data_free'])
					);
					$repair_table[$row['id']] = $row;
				}
			}
	}
	$return['list']=$repair_table;
	$htmls=array(
		'type'=>'list',
		'tabs'=>$tabs,
		'list'=>array('data'=>'list','edit'=>false,'del_one'=>false,'delall'=>false,'list'=>array(
			array('name'=>'id','title'=>'表名','type'=>'string'),
			array('name'=>'size','title'=>'大小','type'=>'string'),
			array('name'=>'type','title'=>'类型','type'=>'string'),
		),'action'=>array(
			array('type'=>'post','op'=>'post_repair','title'=>'修复'),
		),'batch'=>array(
			array('type'=>'post','url'=>'post_repair','title'=>'一键修复'),
		)
		),
	);
	$return['htmls']=$htmls;
	exi($return);
}
if($_RQ['op']=='post_repair'){
	if($_RQ['ids']){
		$table=str_array($_RQ['ids']);
	}else{
		$table=array();
	}
	if($_RQ['id']){
		$table[]=$_RQ['id'];
	}
	if(!$table){
		exi('请先选择要修复的表！','error');
	}
	foreach ($table as $tablename) {
			$sql = "OPTIMIZE TABLE {$tablename}";
			pdo_fetch($sql);
	}
	exi('修复成功！');
}
if ($_RQ['op'] == 'rundb') {
	if (DEVELOP!=1) {
		exi('请先开启开发模式后再使用此功能','error');
	}
	if($_RQ['postdata']){
		$post=get_postdata();
		$sql = $post['sql'];
		pdo_run($sql);
		exi('查询执行成功.', 'success');
	}
	$return['item']=$item;
	$return['htmls']=array(
		'type'=>'edit',
		'desc'=>'运行数据库语句',
		'edit'=>array(
			array('type'=>'text','name'=>'sql','title'=>'数据库语句','help'=>'通过此功能可以直接在数据库中执行特定语句, 或用于调试错误；注意, 这里运行的语句不会有任何返回结果。</br>注意: 此功能可能造成数据破坏, 请谨慎使用. 如果你不清楚他的功能, 请不要使用!'),
		),
	);
	exi($return);
}